
/*
 * Hlpay-Plus aggregate payment system. Copyright
 * (c) 2022-2023 Hlpay Team Copyright has the right of final interpretation.
 */

package com.hlkj.pay.service.common.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.hlkj.framework.common.pojo.LocalContext;
import com.hlkj.framework.common.pojo.PageResult;
import com.hlkj.framework.common.provider.Ip2regionProvider;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.common.util.web.WebFrameworkUtils;
import com.hlkj.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.hlkj.framework.mybatis.core.query.QueryWrapperX;
import com.hlkj.framework.mybatis.core.utils.MyBatisUtils;
import com.hlkj.pay.common.constants.MqConstant;
import com.hlkj.pay.config.ExtRocketMQTemplate;
import com.hlkj.pay.dto.LocalAdminUserRequest;
import com.hlkj.pay.dto.ad.AdStatisticsDto;
import com.hlkj.pay.dto.admin.AdminUserBehaviorLogDto;
import com.hlkj.pay.dto.admin.PermissionQueryDto;
import com.hlkj.pay.dto.admin.QueryUserBehaviorLogDto;
import com.hlkj.pay.dto.common.BusinessLogDto;
import com.hlkj.pay.dto.common.OrderStatusDto;
import com.hlkj.pay.dto.common.QueryChangeLogDto;
import com.hlkj.pay.dto.order.StatisticsDto;
import com.hlkj.pay.enums.AdminUserEnum;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.infrastructure.mapper.admin.BehaviorLogMapper;
import com.hlkj.pay.infrastructure.mapper.admin.LimitRouterLogMapper;
import com.hlkj.pay.infrastructure.mapper.admin.LoginLogMapper;
import com.hlkj.pay.infrastructure.mapper.common.ChangeLogMapper;
import com.hlkj.pay.infrastructure.mapper.common.ConsumeStatisticsMapper;
import com.hlkj.pay.infrastructure.mapper.common.RequestLogMapper;
import com.hlkj.pay.infrastructure.mapper.user.UserRequestLogMapper;
import com.hlkj.pay.infrastructure.model.admin.AdminUserBehaviorLogDO;
import com.hlkj.pay.infrastructure.model.admin.AdminUserLoginLogDO;
import com.hlkj.pay.infrastructure.model.admin.PermissionDO;
import com.hlkj.pay.infrastructure.model.common.ChangeLogDO;
import com.hlkj.pay.infrastructure.model.common.ConsumeStatisticsDO;
import com.hlkj.pay.infrastructure.model.common.LimitRouterLogDO;
import com.hlkj.pay.infrastructure.model.common.RequestLogDO;
import com.hlkj.pay.infrastructure.model.user.UserRequestLogDO;
import com.hlkj.pay.service.CommonSnFilterService;
import com.hlkj.pay.service.admin.IPermissionService;
import com.hlkj.pay.service.common.IBusinessLogService;
import com.hlkj.pay.util.HttpUtils;
import com.hlkj.pay.vo.admin.resp.permission.Meta;

import cn.hutool.extra.servlet.ServletUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.ip2region.core.IpInfo;

/**
 * @author HlpayTeam
 * @date 2024/09/06 15:29
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class BusinessLogServiceImpl extends CommonSnFilterService implements IBusinessLogService {

    private final LoginLogMapper loginLogMapper;

    private final UserRequestLogMapper userRequestLogMapper;

    private final BehaviorLogMapper behaviorLogMapper;

    private final ChangeLogMapper changeLogMapper;

    private final ConsumeStatisticsMapper consumeStatisticsMapper;

    private final IPermissionService permissionService;

    private final Ip2regionProvider ip2regionProvider;

    private final RequestLogMapper requestLogMapper;

    private final ExtRocketMQTemplate extRocketMQTemplate;
    private final LimitRouterLogMapper limitRouterLogMapper;

    @Value("${server.port}")
    private String serverPort;


    @Override
    public void addBusinessChangeLog(String relationCode, Integer type, CommonEnum.CHANGE_LOG_MODEL_TYPE changeLogModelType, Object beforeValue, Object afterValue) {
        BusinessLogDto businessLogDto = new BusinessLogDto();
        businessLogDto.setBeforeValue(JsonUtils.toJsonStringNotNull(beforeValue));
        businessLogDto.setAfterValue(JsonUtils.toJsonStringNotNull(afterValue));
        businessLogDto.setType(type);
        businessLogDto.setModelType(changeLogModelType.getCode());
        businessLogDto.setRelationCode(relationCode);
        try {
            LocalAdminUserRequest localAdminUserRequest = (LocalAdminUserRequest) LocalContext.get();
            businessLogDto.setIpLocation(localAdminUserRequest.getIpLocation());
            businessLogDto.setIp(localAdminUserRequest.getIp());
        }
        catch (Exception e) {

        }
        sendBusinessLogMessage(businessLogDto, CommonEnum.MESSAGE_LOG_TYPE.CHANGE_LOG);
    }

    @Override
    public String addBehaviorLog(String permissionCode) {
        // BusinessLogDto businessLogDO = new BusinessLogDto();
        LocalAdminUserRequest localAdminUserRequest = (LocalAdminUserRequest) LocalContext.get();
        localAdminUserRequest.setPermissionCode(permissionCode);
        // businessLogDO.setLogCode(localAdminUserRequest.getLogCode());
        // businessLogDO.setParams(localAdminUserRequest.getParams());
        // businessLogDO.setStartTime(localAdminUserRequest.getStartTime());
        // sendBusinessLogMessage(businessLogDO,
        // CommonEnum.MESSAGE_LOG_TYPE.BEHAVIOR_LOG);
        return localAdminUserRequest.getLogCode();
    }

    /**
     *
     * @param businessLogDto
     * @param messageLogType
     */
    void sendBusinessLogMessage(BusinessLogDto businessLogDto, CommonEnum.MESSAGE_LOG_TYPE messageLogType) {
        LocalAdminUserRequest localAdminUserRequest = (LocalAdminUserRequest) LocalContext.get();
        Long userId = localAdminUserRequest.getUserId();
        if (businessLogDto == null) {
            businessLogDto = new BusinessLogDto();
        }
        if (!StringUtils.hasText(businessLogDto.getLogCode())) {
            String logCode = localAdminUserRequest.getLogCode();
            businessLogDto.setLogCode(logCode);
        }
        businessLogDto.setUserId(userId);
        businessLogDto.setLogType(messageLogType.getCode());
        String jsonString = JsonUtils.toJsonStringNotNull(businessLogDto);
        if (log.isDebugEnabled()) {
            log.debug("sendBusinessLogMessage:{}", jsonString);
        }
        try {
            extRocketMQTemplate.send(MqConstant.BEHAVIOR_LOG_RECORD_TOPIC, businessLogDto);
        }
        catch (Exception e) {
            log.error("mq 发送异常:{}", e.getMessage(), e);
        }
    }

    @Override
    public void addLoginInLog() {
        try {
            BusinessLogDto businessLogDto = new BusinessLogDto();
            businessLogDto.setAdminUserLoginLogDO(userLoginLogDO());
            sendBusinessLogMessage(businessLogDto, CommonEnum.MESSAGE_LOG_TYPE.LOGIN);
        }
        catch (Exception e) {
            log.error("addLoginInLog error:{}", e.getMessage(), e);
        }
    }

    @Override
    public void addLogOutLog() {
        try {
            BusinessLogDto businessLogDto = new BusinessLogDto();
            businessLogDto.setAdminUserLoginLogDO(userLoginLogDO());
            sendBusinessLogMessage(businessLogDto, CommonEnum.MESSAGE_LOG_TYPE.LOGOUT);
        }
        catch (Exception e) {
            log.error("addLogOutLog error:{}", e.getMessage(), e);
        }
    }

    @Override
    public void consumeBusinessLog(List<BusinessLogDto> bussinessLogList) {
        if (CollectionUtils.isEmpty(bussinessLogList)) {
            return;
        }
        Map<Integer, List<BusinessLogDto>> logTypeMap = bussinessLogList.stream().collect(Collectors.groupingBy(BusinessLogDto::getLogType));
        try {
            logTypeMap.forEach((logType, logTypeBussinessLogs) -> {
                CommonEnum.MESSAGE_LOG_TYPE messageLogType = CommonEnum.MESSAGE_LOG_TYPE.from(logType);
                switch (messageLogType) {
                    case LOGIN:
                        addBatchLoginInLog(logTypeBussinessLogs, AdminUserEnum.BEHAVIOR_LOG_TYPE.LOGIN);
                        break;
                    case LOGOUT:
                        addBatchLoginInLog(logTypeBussinessLogs, AdminUserEnum.BEHAVIOR_LOG_TYPE.LOGOUT);
                        break;
                    case CHANGE_LOG:
                        addBatchBusinessChangeLog(bussinessLogList);
                        break;
                    default:
                }
            });
        }
        catch (Exception e) {
            log.error("bussinessLogs error:{}", e.getMessage(), e);
        }
        finally {
        }
    }

    /**
     *
     * @param businessLogList
     */
    void addBatchLoginInLog(List<BusinessLogDto> businessLogList, AdminUserEnum.BEHAVIOR_LOG_TYPE behaviorLogType) {
        List<AdminUserLoginLogDO> userLoginLogList = new ArrayList<>();
        List<AdminUserBehaviorLogDO> userBehaviorLogList = new ArrayList<>();
        businessLogList.stream().forEach(businessLogDto -> {
            AdminUserLoginLogDO adminUserLoginLogDO = businessLogDto.getAdminUserLoginLogDO();
            if (adminUserLoginLogDO == null) {
                return;
            }
            Integer userId = businessLogDto.getUserId().intValue();
            if (userId != null) {
                adminUserLoginLogDO.setUserId(userId.longValue());
            }
            adminUserLoginLogDO.setType(behaviorLogType.getCode());
            adminUserLoginLogDO.setCreatedBy(userId);
            adminUserLoginLogDO.setUpdatedBy(userId);
            userLoginLogList.add(adminUserLoginLogDO);

            AdminUserBehaviorLogDO adminUserBehaviorLogDO = new AdminUserBehaviorLogDO();
            adminUserBehaviorLogDO.setUserId(adminUserLoginLogDO.getUserId());
            adminUserBehaviorLogDO.setLogCode("");
            adminUserBehaviorLogDO.setModelName("");
            adminUserBehaviorLogDO.setBusinessName("");
            adminUserBehaviorLogDO.setOperationType(behaviorLogType.getCode());
            adminUserBehaviorLogDO.setCreatedBy(userId);
            adminUserBehaviorLogDO.setUpdatedBy(userId);
            userBehaviorLogList.add(adminUserBehaviorLogDO);
        });
        try {
            addBatchLoginLog(userLoginLogList);
            addBatchBehaviorLogDb(userBehaviorLogList);
        }
        catch (Exception e) {
            log.error("addLoginInLog error:{}", e.getMessage(), e);
        }
    }

    public void addBatchBehaviorLogDb(List<AdminUserBehaviorLogDO> userBehaviorLogList) {
        behaviorLogMapper.insertBatch(userBehaviorLogList);
    }

    public void addBatchLoginLog(List<AdminUserLoginLogDO> userLoginLogList) {
        loginLogMapper.insertBatch(userLoginLogList);
    }

    void addBatchBusinessChangeLog(List<BusinessLogDto> businessLogList) {
        List<ChangeLogDO> adminUserChangeLogList = new ArrayList<>();
        businessLogList.stream().forEach(businessLogDto -> {
            ChangeLogDO changeLogDO = new ChangeLogDO();
            changeLogDO.setType(businessLogDto.getType());
            changeLogDO.setModelType(businessLogDto.getModelType());
            changeLogDO.setUserId(businessLogDto.getUserId().longValue());
            changeLogDO.setLogCode(businessLogDto.getLogCode());
            changeLogDO.setBeforeValue(JsonUtils.toJsonString(businessLogDto.getBeforeValue()));
            changeLogDO.setAfterValue(JsonUtils.toJsonString(businessLogDto.getAfterValue()));
            changeLogDO.setCreatedBy(businessLogDto.getUserId().intValue());
            changeLogDO.setUpdatedBy(businessLogDto.getUserId().intValue());
            changeLogDO.setRelationCode(businessLogDto.getRelationCode());
            changeLogDO.setIp(businessLogDto.getIp());
            changeLogDO.setIpLocation(businessLogDto.getIpLocation());
            changeLogDO.setRelationCode(businessLogDto.getRelationCode());
            adminUserChangeLogList.add(changeLogDO);
        });
        addBatchBusinessChangeLogDb(adminUserChangeLogList);
    }

    public void addBatchBusinessChangeLogDb(List<ChangeLogDO> adminUserChangeLogList) {
        changeLogMapper.insertBatch(adminUserChangeLogList);
    }

    @Override
    public PageResult<ChangeLogDO> queryChangeLogList(QueryChangeLogDto queryChangeLog) {
        return changeLogMapper.selectPage(queryChangeLog, buildChangeLogWrapper(queryChangeLog));
    }

    @Override
    public PageResult<AdminUserBehaviorLogDto> queryBehaviorList(QueryUserBehaviorLogDto queryUserBehaviorLog) {
        queryUserBehaviorLog.setSortingFields("behavior.id");
        QueryWrapper<AdminUserBehaviorLogDto> queryWrapper = buildUserQueryWrapper(queryUserBehaviorLog);
        IPage<AdminUserBehaviorLogDto> mpPage = MyBatisUtils.buildPage(queryUserBehaviorLog);
        behaviorLogMapper.queryBehaviorList(mpPage, queryWrapper);
        return new PageResult(mpPage.getRecords(), mpPage.getTotal(), mpPage.getCurrent(), mpPage.getSize(), mpPage.getPages());
    }

    @Override
    public void consumeBusinessPramsLog(List<AdminUserBehaviorLogDO> adminUserBehaviorLogList) {
        if (CollectionUtils.isEmpty(adminUserBehaviorLogList)) {
            return;
        }
        List<String> permissionCodeList = adminUserBehaviorLogList.stream().map(AdminUserBehaviorLogDO::getPermissionCode).collect(Collectors.toList());

        PermissionQueryDto permissionQueryDto = new PermissionQueryDto();
        permissionQueryDto.setPermissionCodeList(permissionCodeList);
        List<PermissionDO> permissionList = permissionService.queryPermissionList(permissionQueryDto);
        if (!CollectionUtils.isEmpty(permissionList)) {
            List<String> parentCodeList = permissionList.stream().map(bussinessLogDO -> bussinessLogDO.getParentCode()).collect(Collectors.toList());
            permissionQueryDto = new PermissionQueryDto();
            permissionQueryDto.setPermissionCodeList(parentCodeList);
            List<PermissionDO> parentPermissionList = permissionService.queryPermissionList(permissionQueryDto);
            permissionList.addAll(parentPermissionList);
        }
        else {
            permissionList = new ArrayList<>();
        }
        // cover to map
        Map<String, PermissionDO> permissionMap = permissionList.stream().collect(Collectors.toMap(PermissionDO::getCode, Function.identity(), (t1, t2) -> t2));
        Map<String, PermissionDO> finalPermissionMap = permissionMap;
        adminUserBehaviorLogList.stream().forEach(userBehaviorLogDO -> {
            PermissionDO permissionDO = permissionMap.get(userBehaviorLogDO.getPermissionCode());
            if (permissionDO != null) {
                userBehaviorLogDO.setBusinessName(name(permissionDO));
                if (StringUtils.hasText(permissionDO.getParentCode())) {
                    PermissionDO parentPermission = finalPermissionMap.get(permissionDO.getParentCode());
                    if (parentPermission != null) {
                        userBehaviorLogDO.setModelName(name(parentPermission));
                    }
                }
            }
            userBehaviorLogDO.setCreatedBy(userBehaviorLogDO.getUserId().intValue());
            userBehaviorLogDO.setUpdatedBy(userBehaviorLogDO.getUserId().intValue());
        });
        addBatchBehaviorLogDb(adminUserBehaviorLogList);
    }

    @Override
    public void consumeRequestLog(List<RequestLogDO> requestLogList) {
        requestLogMapper.insertBatch(requestLogList);
    }

    @Override
    public void consumeOrderStatusLog(String topic, OrderStatusDto orderStatusDto) {
        ConsumeStatisticsDO consumeStatisticsDO = new ConsumeStatisticsDO();
        consumeStatisticsDO.setTopic(topic);
        consumeStatisticsDO.setOrderNo(orderStatusDto.getOrderNo());
        consumeStatisticsDO.setSendTime(orderStatusDto.getDelaySendTime());
        consumeStatisticsDO.setDelayLevel(orderStatusDto.getDelayLevel());
        if (consumeStatisticsDO.getSendTime() != null) {
            consumeStatisticsDO.setCostTime(System.currentTimeMillis() - consumeStatisticsDO.getSendTime());
        }
        consumeStatisticsDO.setType(orderStatusDto.getOrderStatusType());
        consumeStatisticsDO.setMessageBody(JsonUtils.toJsonStr(orderStatusDto));
        consumeStatisticsDO.setConsumerCount(orderStatusDto.getTotalConsumerCount());
        consumeStatisticsDO.setBatchId(orderStatusDto.getOrderNo() + "-" + serverPort);
        consumeStatisticsMapper.insert(consumeStatisticsDO);
    }

    @Override
    public void consumeAdOrderStatusBatchLog(String topic, List<AdStatisticsDto> adStatisticsDtos) {
        List<ConsumeStatisticsDO> consumeStatisticsList = new ArrayList<>(adStatisticsDtos.size() * 3 / 2);
        String batchId = UUID.randomUUID().toString();
        adStatisticsDtos.stream().forEach(adStatisticsDto -> {
            ConsumeStatisticsDO consumeStatisticsDO = new ConsumeStatisticsDO();
            consumeStatisticsDO.setTopic(topic);
            consumeStatisticsDO.setOrderNo(adStatisticsDto.getOrderNo());
            consumeStatisticsDO.setSendTime(adStatisticsDto.getSendTime());
            // consumeStatisticsDO.setDelayLevel();
            if (consumeStatisticsDO.getSendTime() != null) {
                consumeStatisticsDO.setCostTime(System.currentTimeMillis() - consumeStatisticsDO.getSendTime());
            }
            consumeStatisticsDO.setType(adStatisticsDto.getStatisticsType());
            consumeStatisticsDO.setMessageBody(JsonUtils.toJsonStr(adStatisticsDto));
            consumeStatisticsDO.setConsumerCount(adStatisticsDtos.size());
            consumeStatisticsDO.setBatchId(batchId + "-" + serverPort);
            consumeStatisticsList.add(consumeStatisticsDO);
        });
        consumeStatisticsMapper.insertBatch(consumeStatisticsList);
    }

    @Override
    public void consumeOrderStatusBatchLog(String topic, List<StatisticsDto> adStatisticsDtos) {
        List<ConsumeStatisticsDO> consumeStatisticsList = new ArrayList<>(adStatisticsDtos.size() * 3 / 2);
        String batchId = UUID.randomUUID().toString();
        adStatisticsDtos.stream().forEach(adStatisticsDto -> {
            ConsumeStatisticsDO consumeStatisticsDO = new ConsumeStatisticsDO();
            consumeStatisticsDO.setTopic(topic);
            consumeStatisticsDO.setOrderNo(adStatisticsDto.getOrderNo());
            consumeStatisticsDO.setSendTime(adStatisticsDto.getSendTime());
            // consumeStatisticsDO.setDelayLevel();
            if (consumeStatisticsDO.getSendTime() != null) {
                consumeStatisticsDO.setCostTime(System.currentTimeMillis() - consumeStatisticsDO.getSendTime());
            }
            consumeStatisticsDO.setType(adStatisticsDto.getStatisticsType());
            consumeStatisticsDO.setMessageBody(JsonUtils.toJsonStr(adStatisticsDto));
            consumeStatisticsDO.setConsumerCount(adStatisticsDtos.size());
            consumeStatisticsDO.setBatchId(batchId + "-" + serverPort);
            consumeStatisticsDO.setSn(adStatisticsDto.getSn());
            consumeStatisticsList.add(consumeStatisticsDO);
        });
        consumeStatisticsMapper.insertBatch(consumeStatisticsList);
    }

    @Override
    public void addLimitRouterLog(LimitRouterLogDO limitRouterLogDO) {
        limitRouterLogMapper.insert(limitRouterLogDO);
    }

    @Override
    public void consumeClientBusinessPramsLog(List<UserRequestLogDO> userRequestLogList) {
        userRequestLogMapper.insertBatch(userRequestLogList);
    }

    private AdminUserLoginLogDO userLoginLogDO() {
        HttpServletRequest request = WebFrameworkUtils.getRequest();
        AdminUserLoginLogDO adminUserLoginLogDO = new AdminUserLoginLogDO();
        adminUserLoginLogDO.setIp(ServletUtil.getClientIP(request, null));
        try {
            IpInfo ipInfo = ip2regionProvider.getIp2regionSearcher().memorySearch(adminUserLoginLogDO.getIp());
            if (ipInfo != null) {
                adminUserLoginLogDO.setIpLocation(ipInfo.getAddress());
            }
        }
        catch (Exception e) {

        }
        adminUserLoginLogDO.setBrowserType(HttpUtils.getBrowserName(request));
        adminUserLoginLogDO.setBrowserVersion(HttpUtils.getBrowserVersion(request));
        adminUserLoginLogDO.setOperatingSystem(HttpUtils.getOsName(request));
        return adminUserLoginLogDO;
    }

    /**
     * 查询条件封装
     * 
     * @param queryUserBehaviorLogDto
     * @return
     */
    QueryWrapper<AdminUserBehaviorLogDto> buildUserQueryWrapper(QueryUserBehaviorLogDto queryUserBehaviorLogDto) {
        filterSn(queryUserBehaviorLogDto);
        QueryWrapperX<AdminUserBehaviorLogDto> queryWrapper = new QueryWrapperX<>();
        queryWrapper.likeIfPresent( "au.name", queryUserBehaviorLogDto.getLikeName());
        queryWrapper.likeIfPresent("au.user_name", queryUserBehaviorLogDto.getLikeUserName());
        queryWrapper.likeIfPresent( "au.sn", queryUserBehaviorLogDto.getLikeSn());
        queryWrapper.eqIfPresent( "au.sn", queryUserBehaviorLogDto.getSn());
        queryWrapper.inIfPresent( "au.sn", queryUserBehaviorLogDto.getSnList());
        queryWrapper.eqIfPresent( "au.sys_type", queryUserBehaviorLogDto.getSysType());
        queryWrapper.eqIfPresent( "au.id", queryUserBehaviorLogDto.getUserId());
        queryWrapper.likeIfPresent( "behavior.model_name", queryUserBehaviorLogDto.getLikeModelName());
        queryWrapper.geIfPresent( "behavior.create_time", queryUserBehaviorLogDto.getStartQueryTime());
        queryWrapper.leIfPresent( "behavior.create_time", queryUserBehaviorLogDto.getEndQueryTime());

        return queryWrapper;
    }

    /**
     * 查询条件封装
     * 
     * @param queryChangeLogDto
     * @return
     */
    LambdaQueryWrapper<ChangeLogDO> buildChangeLogWrapper(QueryChangeLogDto queryChangeLogDto) {
        LambdaQueryWrapperX<ChangeLogDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(ChangeLogDO::getUserId, queryChangeLogDto.getUserId());
        queryWrapper.likeIfPresent(ChangeLogDO::getLogCode, queryChangeLogDto.getLogCode());
        queryWrapper.eqIfPresent(ChangeLogDO::getRelationCode, queryChangeLogDto.getRelationCode());
        return queryWrapper;
    }

    String name(PermissionDO permissionDO) {
        if (permissionDO != null) {
            String metaInfo = permissionDO.getMetaInfo();
            Meta meta = JsonUtils.parseObject(metaInfo, Meta.class);
            return meta.getTitle();
        }
        return null;
    }
}
